
-- Lua 元表
--[[
    方便两个table 进行操作。
    setmetatable(table,metatable): 给 指定 table 设置 元表( metatable)，
        如果元表(metatable)中存在 __metatable 键值，setmetatable 会失败。
    getmetatable(table): 返回对象的元表(metatable)。
--]]

mytable = {}                          -- 普通表
mymetatable = {}                      -- 元表
setmetatable(mytable,mymetatable)     -- 把 mymetatable 设为 mytable 的元表

-- 简写
mytable = setmetatable({},{})

-- __index 元方法 : 查找元素
--[[
    当你通过键来访问 table 的时候，如果这个键没有值，那么Lua就会寻找该table的metatable（假定有metatable）中的__index 键。
    如果__index是一个表格，Lua会在表格中查找相应的键，如果在元表中找不到，就返回nil
    如果__index是一个函数，就会执行这个函数，此函数有两个参数 ： table 和 key
--]]
-- __index 是一个table
t = setmetatable({}, { __index = { foo = 3 } } )
print( t.foo) -- table中找不到 ，就找metatable
print( t.bar) -- 元表中找不到，说明不存在，返回 nil

-- __index 是一个function
mytable = setmetatable({key1 = "value1"}, {
    __index = function(mytable, key)
        if key == "key2" then
            return "metatablevalue"
        else
            return nil
        end
    end
})

print(mytable.key1,mytable.key2)
print('--------------------------------')

-- __newindex 元方法 ：更新表
--[[
    当你给表的一个缺少的索引赋值，解释器就会查找__newindex 元方法：如果存在则调用这个函数而不进行赋值操作。
--]]
mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })

print(mytable.key1)

mytable.newkey = "新值2"
print(mytable.newkey,mymetatable.newkey) -- table 不存在 newkey 键，就会调用 metatable 的赋值方法给元表赋值

mytable.key1 = "新值1"
print(mytable.key1,mymetatable.key1) -- table 存在 key1 键，就会调用 table 的赋值方法给表赋值
print('--------------------------------')

mytable = setmetatable({key1 = "value1"}, {
    __newindex = function(mytable, key, value)
        rawset(mytable, key, "\""..value.."\"")
    end
})

mytable.key1 = "new value"
mytable.key2 = 4

print(mytable.key1,mytable.key2)
print('--------------------------------')

-- 为表添加操作符
-- 计算表中最大值，table.maxn在Lua5.2以上版本中已无法使用
-- 自定义计算表中最大键值函数 table_maxn，即计算表的元素个数
function table_maxn(t)
    local mn = 0
    for k, v in pairs(t) do
        if mn < k then
            mn = k
        end
    end
    return mn
end

-- 两表相加操作
mytable = setmetatable({ 1, 2, 3 }, {
    __add = function(mytable, newtable)
        for i = 1, table_maxn(newtable) do
            table.insert(mytable, table_maxn(mytable)+1,newtable[i]) -- 在table中的指定位置插入元素
        end
        return mytable
    end
})

secondtable = {4,5,6}

mytable = mytable + secondtable -- 两个表相加
for k,v in ipairs(mytable) do
    print(k,v)
end
print('--------------------------------')
--[[
    __add	对应的运算符 '+'.
    __sub	对应的运算符 '-'.
    __mul	对应的运算符 '*'.
    __div	对应的运算符 '/'.
    __mod	对应的运算符 '%'.
    __unm	对应的运算符 '-'.
    __concat	对应的运算符 '..'.
    __eq	对应的运算符 '=='.
    __lt	对应的运算符 '<'.
    __le	对应的运算符 '<='.

--]]


-- __call 元方法
-- 定义元方法__call
mytable = setmetatable({10}, {
    __call = function(mytable, newtable)
        sum = 0
        for i = 1, table_maxn(mytable) do
            sum = sum + mytable[i]
        end
        for i = 1, table_maxn(newtable) do
            sum = sum + newtable[i]
        end
        return sum
    end
})
newtable = {10,20,30}
print(mytable(newtable))
print('--------------------------------')

-- __tostring 元方法 : 修改表的输出行为

mytable = setmetatable({ 10, 20, 30 }, {
    __tostring = function(mytable)
        sum = 0
        for k, v in pairs(mytable) do
            sum = sum + v
        end
        return "表所有元素的和为 " .. sum
    end
})
print(mytable)
